// use crate::Loc;
use std::path::Path;
use std::sync::Mutex;

use regex::Regex;

use rusqlite::{params, Connection};
use rusqlite::NO_PARAMS;
// use time::Timespec;
// use crate::ChainData;
use dirs::home_dir;
use crate::Color;

lazy_static!{
    static ref TMP_HOST:Mutex<Vec<String>> = {
        Mutex::new(
            vec!["BEGIN;".to_string()]
        )
    };
}

pub trait LocalHost{
    fn db_in(&self) -> String;
    fn search_host(&self, key:&str) -> Vec<String>;
    fn add_host(&self, loc:&str) -> usize;
    fn remove_host(&self, key:&str) -> usize;
    fn len_host(&self) -> usize;
    fn commit(&self);
}
fn open_by_str(db_str:&str) -> Option<Connection>{
    if let Ok(r) = Regex::new(r#"db://(.+)\|"#){
        for cap in r.captures_iter(db_str){
            let re_str = cap[1].to_string();
            let p = Path::new(&re_str);
            if p.exists(){
                
                return match Connection::open(p){
                    Ok(a) => Some(a),
                    Err(e) => {
                        log::error!("open db: |failed :{}", e);
                        None
                    }
                };
            }
        }
    }else{
        log::error!("re error: {}", "");
    };
    None
}

impl <'a>LocalHost for &'a str{
    fn len_host(&self) -> usize{
        if let Some(db) = open_by_str(self){
            match db.prepare(r#"SELECT COUNT(*) FROM Host ;"#){
                Ok(mut u) =>{
                    if let Ok(mut dbs) = u.query(params![]){
                        if let Some(s) = dbs.next().unwrap(){
                            let i:i64 = s.get(0).expect("get count error");
                            return i as usize;
                            // i.as_i32()
                        }
                        
                    }
                },
                _ => {}
            }
        };
        0
    }
    fn commit(&self) {
        let mut t = TMP_HOST.lock().expect("lock failed");
        if let Some(db) = open_by_str(self){
            t.push("COMMIT;".to_string());
            let s =  t.join("\n");
            // log::info!("{}",s.blue());
            db.execute_batch(&s).unwrap();
            *t = Vec::new();
        }
    }
    fn db_in(&self) -> String{
        if self.trim().starts_with("~"){
            if let Some(home) = home_dir().unwrap().to_str(){
                let ss = self.replace("~", home);
                let p = Path::new(&ss);
                if p.exists(){
                    return format!("db://{}|", p.to_str().unwrap());
                }
            }
        }else{
            let p = Path::new(self);
            if p.exists(){
                return format!("db://{}|", p.to_str().unwrap());
            }
        }
        self.to_string()
    }
    fn add_host(&self, host:&str) -> usize{
        if !host.starts_with("chain://"){
            return 0;
        }
        let mut if_has_host = false;
        if let Some(db) = open_by_str(self){
            let host = host.replace("chain://","");
            if let Ok(mut u) = db.prepare(r#"SELECT name FROM sqlite_master WHERE type='table' AND name='Host';"#){
                if let Ok(mut dbs) = u.query(params![]){
                    if let Ok(e) = dbs.next(){
                        if let Some(_) = e{
                            if_has_host = true;
                            // if self.search_host(&host.clone()).len() > 0{
                            //     log::info!("aleady host : {}", host.yellow());
                            //     if_same_host = true;
                            // }
                        }
                    }
                }
            }
            // let e = db.execute("SELECT COUNT(*) FROM Host;",params![]).expect("count error");
            if !if_has_host{
                
                db.execute("CREATE TABLE Host 
                        (id INTEGER PRIMARY KEY AUTOINCREMENT,
                            `host` TEXT,
                            `os` TEXT);",params![]).expect("crate db failed");
            }
            {
                let mut l = TMP_HOST.lock().expect("tmp lock for save sql string");
                let host = format!(r#"INSERT INTO Host (host, os)
                VALUES ("{}", "{}");"#,host.trim(), "Loc");
                if !l.contains(&host){
                    l.push(host);
                }
                

            }
            self.len_host()
            
        }else{
            0
        }
    }

    fn remove_host(&self, key:&str) -> usize{
        if let Some(db) = open_by_str(self){
            let sq = format!(r#"DELETE FROM Host WHERE `host` like "%{}%" ;"#,key.trim());
            log::info!("{}", sq.yellow());
            match db.execute(&sq, NO_PARAMS){
                Ok(_) => {
                    self.len_host()
                },
                _ => {
                    0
                }
            }
        }else{
            0
        }
    }
    fn search_host(&self, key:&str) -> Vec<String>{
        let mut out:Vec<String> = Vec::new();
        if let Some(db) = open_by_str(self){
            
            let sq = format!(r#"SELECT `host` FROM Host WHERE `host` like "%{}%" ;"#, key.trim());
            // log::info!("query : {}", sq.green());
            match db.prepare(&sq){
                Ok(mut s) => {
                    // log::info!("query ok");
                    if let Ok(mut res) = s.query(params![]){
                        while let Some(row) = res.next().unwrap(){
                            let s:String = row.get(0).unwrap();
                            // log::info!("search : {}", s.green());
                            let l = format!("chain://{}",s);
                            out.push(l);
                        }
                        
                    };
                    out
                },
                _ => {
                    log::error!("prepared error!");
                    out
                }
             }
        }else{
            out
        }
    }
}


impl LocalHost for String{
    fn commit(&self) {
        self.as_str().commit()
    }
    fn len_host(&self) -> usize{
        self.as_str().len_host()
    }
    fn db_in(&self) -> String{
        self.as_str().db_in()
    }
    fn search_host(&self,key:&str) -> Vec<String>{
        self.as_str().search_host(key)
    }
    fn add_host(&self, host:&str) -> usize{
        self.as_str().add_host(host)
    }
    fn remove_host(&self, key:&str) -> usize{
        self.as_str().remove_host(key)
    }
}